package com.taxshare.minio.utils;

import com.taxshare.minio.dto.MinIOUploadResDTO;
import com.taxshare.minio.exception.MinIOException;
import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Component
@Slf4j
@DependsOn("minIOConfig")
public class MinIOUtils {
    @Autowired
    private MinioClient instance;

    private static final String SEPARATOR_DOT = ".";

    private static final String SEPARATOR_ACROSS = "-";

    private static final String SEPARATOR_STR = "";

    /**
     * @param bucketName
     * @return boolean
     * @Description 判断 bucket是否存在
     */
    public boolean bucketExists(String bucketName) {
        try {
            return instance.bucketExists(bucketName);
        } catch (Exception e) {
            log.error("MinIO异常：", e);
        }
        return false;
    }

    /**
     * 创建 bucket
     *
     * @param bucketName
     */
    public void makeBucket(String bucketName) {
        try {
            boolean isExist = instance.bucketExists(bucketName);
            if (!isExist) {
                instance.makeBucket(bucketName);
            }
        } catch (Exception e) {
            log.error("MinIO异常：", e);
        }
    }

    /**
     * @param bucketName
     * @return boolean
     * @Description 删除桶
     */
    public boolean removeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        try {
            if (flag) {
                Iterable<Result<Item>> myObjects = listObjects(bucketName);
                for (Result<Item> result : myObjects) {
                    Item item = result.get();
                    // 有对象文件，则删除失败
                    if (item.size() > 0) {
                        return false;
                    }
                }
                // 删除存储桶，注意，只有存储桶为空时才能删除成功。
                instance.removeBucket(bucketName);
                flag = bucketExists(bucketName);
                if (!flag) {
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("删除bucket:" + bucketName + "失败：", e);
        }
        return false;
    }

    /**
     * @param
     * @return java.util.List<java.lang.String>
     * @Description 获取文件存储服务的所有存储桶名称
     */
    public List<String> listBucketNames() {
        List<Bucket> bucketList = listBuckets();
        List<String> bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }

    /**
     * @param
     * @return java.util.List<io.minio.messages.Bucket>
     * @Description 列出所有存储桶
     */
    public List<Bucket> listBuckets() {
        List<Bucket> buckets = new ArrayList<>();
        try {
            buckets = instance.listBuckets();
        } catch (Exception e) {
            log.error(">>>>>查询bukets失败：", e);
        }
        return buckets;
    }

    /**
     * @param bucketName
     * @return java.util.List<java.lang.String>
     * @Description 列出存储桶中的所有对象名称
     */
    public List<String> listObjectNames(String bucketName) {
        List<String> listObjectNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        try {
            if (flag) {
                Iterable<Result<Item>> myObjects = listObjects(bucketName);
                for (Result<Item> result : myObjects) {
                    Item item = result.get();
                    listObjectNames.add(item.objectName());
                }
            }
        } catch (Exception e) {
            log.error(">>>>>查询bukets中ObjectName失败：", e);
        }
        return listObjectNames;
    }

    /**
     * @param bucketName
     * @return java.lang.Iterable<io.minio.Result<io.minio.messages.Item>>
     * @Description 列出存储桶中的所有对象
     */
    public Iterable<Result<Item>> listObjects(String bucketName) {
        boolean flag = bucketExists(bucketName);
        try {
            if (flag) {
                return instance.listObjects(bucketName);
            }
        } catch (Exception e) {
            log.error(">>>>>查询bukets中Objects失败：", e);
        }

        return null;
    }


    /**
     * @param multipartFile
     * @param isGenerateFileName(是否生成文件名)
     * @return java.lang.String
     * @Description 文件上传
     */
    public MinIOUploadResDTO upload(String bucketName, String directory, MultipartFile multipartFile,boolean isGenerateFileName){
        String fileName=multipartFile.getOriginalFilename();
        if(isGenerateFileName){
            fileName=generateFileName(fileName);
        }
        InputStream inputStream=null;
        try {
            inputStream = multipartFile.getInputStream();
            PutObjectOptions putObjectOptions = new PutObjectOptions(inputStream.available(), -1);
            // 文件的ContentType
            putObjectOptions.setContentType(multipartFile.getContentType());
            instance.putObject(bucketName, directory+"/"+fileName, inputStream, putObjectOptions);
            // 返回生成文件名、访问路径
            return new MinIOUploadResDTO(fileName,instance.presignedGetObject(bucketName, directory+fileName));
        } catch (Exception e) {
            log.error("文件上传失败：", e);
            throw new MinIOException("文件上传失败", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("文件上传失败：", e);
                }
            }
        }


    }

    /**
     * @param response
     * @return java.lang.String
     * @Description 下载文件
     */
    public void download(HttpServletResponse response, String bucketName, String directory,String fileName){
        InputStream fileInputStream = null;
        try {
            fileInputStream=instance.getObject(bucketName, directory+"/"+fileName);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            IOUtils.copy(fileInputStream, response.getOutputStream());
        }catch (Exception e){
           throw new MinIOException("文件下载失败：",e);
        }finally {
            if(fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    log.error("文件下载失败：",e);
                }
            }
        }
    }

    /**
     * 删除一个文件
     *
     * @param bucketName
     * @param objectName
     */
    public boolean removeObject(String bucketName, String directory,String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            try {
                String path=objectName;
                if(StringUtils.isNotEmpty(directory)){
                    path=directory+"/"+objectName;
                }
                instance.removeObject(bucketName, path);
                return true;
            }catch (Exception e){
                log.error("文件删除失败：",e);
                throw new MinIOException("文件删除失败：",e);
            }

        }
        return false;

    }

    /**
     * @param bucketName
     * @param objectNames
     * @return java.util.List<java.lang.String>
     * @Description 删除指定桶的多个文件对象, 返回删除错误的对象列表，全部删除成功，返回空列表
     */
    public List<String> removeObject(String bucketName, List<String> objectNames) {
        List<String> deleteErrorNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            try {
                Iterable<Result<DeleteError>> results = instance.removeObjects(bucketName, objectNames);
                for (Result<DeleteError> result : results) {
                    DeleteError error = result.get();
                    deleteErrorNames.add(error.objectName());
                }
            }catch (Exception e){
                log.error("文件删除失败：",e);
                throw new MinIOException("文件删除失败：",e);
            }
        }
        return deleteErrorNames;
    }


    /**
     * @param originalFileName
     * @return java.lang.String
     * @Description 生成上传文件名
     */
    private String generateFileName(String originalFileName) {
        String suffix = originalFileName;
        if (originalFileName.contains(SEPARATOR_DOT)) {
            suffix = originalFileName.substring(originalFileName.lastIndexOf(SEPARATOR_DOT));
        }
        return UUID.randomUUID().toString().replace(SEPARATOR_ACROSS, SEPARATOR_STR).toUpperCase() + suffix;
    }
}
